import configparser
from playwright.sync_api import sync_playwright
import threading
import os
import sys
import time
from modules.logger_config import setup_logger

# 使用统一的日志配置
logger = setup_logger('LoginManager')

def find_chrome_executable(root_dir):
    """递归查找chrome.exe文件"""
    if not os.path.exists(root_dir):
        return None
    
    for dirpath, _, filenames in os.walk(root_dir):
        if 'chrome.exe' in filenames:
            return os.path.join(dirpath, 'chrome.exe')
    return None

class LoginManager:
    def __init__(self, config_path):
        self.config = self._load_config(config_path)
        self.cookies = None
        # 加载浏览器配置
        self.browser_config = self._load_browser_config(config_path)
        # 创建线程本地存储，用于存储每个线程的Playwright实例
        self._local = threading.local()

    def _find_chrome_executable(self):
        """查找Chrome可执行文件的完整路径"""
        # 获取当前工作目录（exe所在目录）
        current_dir = os.getcwd()
        logger.info(f"当前工作目录（exe所在目录）: {current_dir}")
        
        # 1. 优先在当前目录（exe所在目录）递归查找
        logger.info("优先在exe所在目录递归查找chrome.exe...")
        chrome_path = find_chrome_executable(current_dir)
        if chrome_path:
            logger.info(f"在exe所在目录找到Chrome: {chrome_path}")
            return chrome_path
        
        # 2. 检查exe目录下的常见浏览器文件夹结构
        common_browser_paths = [
            os.path.join(current_dir, 'chrome.exe'),
            os.path.join(current_dir, 'chromium', 'chrome.exe'),
            os.path.join(current_dir, 'playwright', '.cache', 'ms-playwright', 'chromium-1187', 'chrome-win', 'chrome.exe'),
            os.path.join(current_dir, '_internal', 'playwright', '.cache', 'ms-playwright', 'chromium-1187', 'chrome-win', 'chrome.exe'),
            os.path.join(current_dir, 'browser', 'chrome.exe'),
            os.path.join(current_dir, 'browsers', 'chrome', 'chrome.exe')
        ]
        
        for path in common_browser_paths:
            if os.path.exists(path):
                logger.info(f"在常见路径找到Chrome: {path}")
                return path
        
        # 3. 检查PyInstaller临时目录（如果存在）
        if hasattr(sys, '_MEIPASS'):
            logger.info(f"检查PyInstaller临时目录: {sys._MEIPASS}")
            chrome_path = find_chrome_executable(sys._MEIPASS)
            if chrome_path:
                logger.info(f"在MEIPASS找到Chrome: {chrome_path}")
                return chrome_path
        
        # 4. 检查系统默认安装路径
        system_paths = [
            os.path.join(os.getenv('ProgramFiles', 'C:\Program Files'), 'Google', 'Chrome', 'Application', 'chrome.exe'),
            os.path.join(os.getenv('ProgramFiles(x86)', 'C:\Program Files (x86)'), 'Google', 'Chrome', 'Application', 'chrome.exe'),
            os.path.join(os.path.expanduser('~'), 'AppData', 'Local', 'Google', 'Chrome', 'Application', 'chrome.exe')
        ]
        
        for path in system_paths:
            if os.path.exists(path):
                logger.info(f"在系统路径找到Chrome: {path}")
                return path
        
        logger.error("未找到Chrome可执行文件")
        return None

    def _load_config(self, config_path):
        """加载登录配置"""
        config = configparser.ConfigParser()
        config.read(config_path, encoding='utf-8')
        return config['Login']
    
    def _load_browser_config(self, config_path):
        """加载浏览器配置"""
        config = configparser.ConfigParser()
        config.read(config_path, encoding='utf-8')
        return config['Browser']

    def _get_browser_launch_options(self):
        """获取浏览器启动参数"""
        # 转换模式配置为布尔值：headless模式对应True，headed对应False
        headless_mode = self.browser_config['mode'].lower() == 'headless'
        
        options = {
            'headless': headless_mode,
            'args': ['--no-sandbox'] if headless_mode else []
        }
        
        # 强制设置可执行文件路径
        chrome_path = self._find_chrome_executable()
        if chrome_path:
            options['executable_path'] = chrome_path
        else:
            print("警告：未找到Chrome可执行文件，将尝试使用系统默认浏览器")
        
        return options

    def _get_browser_type(self, playwright):
        """根据配置获取浏览器类型"""
        browser_type = self.browser_config['type'].lower()
        if browser_type == 'firefox':
            return playwright.firefox
        elif browser_type == 'webkit':
            return playwright.webkit
        else:  # 默认chromium
            return playwright.chromium

    def perform_login(self, auto_close=False):
        """执行登录并获取Cookie（支持浏览器模式配置和自动关闭控制）"""
        import os
        import sys
        
        # 同时检查配置文件是否存在
        config_path = 'config.ini'
        if not os.path.exists(config_path):
            logger.warning(f"警告: 未找到配置文件 {config_path}")
            # 尝试从其他位置查找配置文件
            if hasattr(sys, '_MEIPASS'):
                config_path = os.path.join(sys._MEIPASS, 'config.ini')
                if os.path.exists(config_path):
                    logger.info(f"在MEIPASS中找到配置文件: {config_path}")
                    # 重新加载配置
                    self.config = self._load_config(config_path)
                    self.browser_config = self._load_browser_config(config_path)
        
        # 为当前线程创建Playwright实例
        p = sync_playwright().start()
        browser = None
        context = None
        page = None
        
        try:
            # 保存Playwright实例到线程本地存储
            self._local.playwright = p
            
            # 获取浏览器类型和启动参数
            browser_type = self._get_browser_type(p)
            launch_options = self._get_browser_launch_options()
            
            logger.info(f"浏览器启动选项: {launch_options}")
            
            # 启动浏览器
            browser = browser_type.launch(**launch_options)
            context = browser.new_context()
            page = context.new_page()
            
            # 访问登录页面
            page.goto(self.config['url'])
            
            # 添加页面加载等待
            page.wait_for_load_state('domcontentloaded')
 
            time.sleep(3)
            page.fill(self.config['username_field'], '')
            # 填写登录信息
            page.type(
                self.config['username_field'],
                self.config['username'],
                delay=50
                )
            logger.info(f"填充登录页面完成")
            time.sleep(3)
            page.fill( self.config['password_field'],'')
            
            page.type(
                self.config['password_field'],
                self.config['password'],
                delay=50
            )
            time.sleep(3)
            # 提交登录
            page.click(self.config['submit_button'])
            
            # 修复timeout处理
            timeout_str = self.config['timeout'].split('#')[0].strip()
            
            # 使用更直接的方式检测URL变化
            start_time = time.time()
            timeout_seconds = int(timeout_str) / 1000  # 转换为秒
            success_indicator = self.config['login_success_indicator']
            
            logger.info(f"开始检测URL，寻找包含: {success_indicator} 的地址")
            
            while time.time() - start_time < timeout_seconds:
                current_url = page.url
                logger.info(f"当前URL: {current_url}")
                
                # 直接检查URL是否包含指定字符串
                if success_indicator in current_url:
                    logger.info(f"✓ 检测成功！URL包含: {success_indicator}")
                    break
                
                # 等待一小段时间再检查
                page.wait_for_timeout(500)
            else:
                # 超时
                logger.error(f"✗ 检测超时，URL中未找到: {success_indicator}")
                return False
            
            # 获取所有Cookie（包含HttpOnly）
            self.cookies = context.cookies()
            logger.info(f"登录成功，获取到的cookie数量: {len(self.cookies)}")
            
            logger.info(f"登录成功，使用{self.browser_config['type']}浏览器（{self.browser_config['mode']}模式）")
            
            # 如果不需要自动关闭，保存浏览器实例
            if not auto_close:
                self._browser = browser
                self._context = context
                logger.info("浏览器将保持打开状态")
            else:
                # 自动关闭场景，在当前线程中清理资源
                logger.info("自动关闭浏览器...")
                # 按照正确的顺序关闭资源
                try:
                    if page:
                        page.close()
                    if context:
                        context.close()
                    if browser:
                        browser.close()
                    if p:
                        p.stop()
                    # 清除线程本地存储中的引用
                    if hasattr(self._local, 'playwright'):
                        delattr(self._local, 'playwright')
                    # 清除实例变量中的引用
                    if hasattr(self, '_browser'):
                        delattr(self, '_browser')
                    if hasattr(self, '_context'):
                        delattr(self, '_context')
                    logger.info("浏览器已成功关闭")
                except Exception as close_e:
                    logger.error(f"关闭浏览器资源时发生错误: {str(close_e)}")
                    # 即使关闭失败，也不应该影响登录结果，因为登录已经成功
                
            return True
        except Exception as e:
            logger.error(f"登录失败: {str(e)}", exc_info=True)
            # 发生异常时确保资源被释放
            self._safe_cleanup_resources(p, browser, context, page)
            return False
    
    def _safe_cleanup_resources(self, playwright=None, browser=None, context=None, page=None):
        """安全清理浏览器资源，捕获所有可能的异常"""
        resources = [
            ('page', page, lambda x: x.close()),
            ('context', context, lambda x: x.close()),
            ('browser', browser, lambda x: x.close()),
            ('playwright', playwright, lambda x: x.stop())
        ]
        
        for name, resource, close_func in resources:
            if resource:
                try:
                    close_func(resource)
                    logger.debug(f"成功关闭{name}")
                except Exception as e:
                    logger.error(f"关闭{name}失败: {str(e)}")
        
        # 清理引用
        try:
            if hasattr(self._local, 'playwright'):
                delattr(self._local, 'playwright')
            if hasattr(self, '_browser'):
                delattr(self, '_browser')
            if hasattr(self, '_context'):
                delattr(self, '_context')
        except Exception as e:
            logger.error(f"清理资源引用失败: {str(e)}")

    def close_browser(self):
        """手动关闭浏览器，安全处理跨线程/greenlet操作"""
        try:
            # 安全关闭浏览器资源，捕获可能的跨greenlet错误
            success = True
            
            # 关闭上下文
            if hasattr(self, '_context') and self._context:
                try:
                    self._context.close()
                    self._context = None
                except Exception as ctx_e:
                    logger.error(f"关闭浏览器上下文失败: {str(ctx_e)}")
                    success = False
            
            # 关闭浏览器
            if hasattr(self, '_browser') and self._browser:
                try:
                    self._browser.close()
                    self._browser = None
                except Exception as browser_e:
                    logger.error(f"关闭浏览器实例失败: {str(browser_e)}")
                    success = False
            
            # 安全停止Playwright - 这是最容易出现跨greenlet错误的地方
            # 注意：在不同greenlet中停止playwright可能会引发错误，但不应导致程序崩溃
            try:
                if hasattr(self._local, 'playwright'):
                    self._local.playwright.stop()
                    delattr(self._local, 'playwright')
            except Exception as pw_e:
                # 特殊处理greenlet切换错误
                if "Cannot switch to a different thread" in str(pw_e) or "greenlet" in str(pw_e).lower():
                    logger.warning(f"Playwright已在其他greenlet中停止，无需重复操作")
                else:
                    logger.error(f"停止Playwright失败: {str(pw_e)}")
                # 即使playwright停止失败，只要浏览器和上下文已关闭，我们仍认为操作基本成功
            
            if success:
                logger.info("浏览器已手动关闭")
            else:
                logger.warning("浏览器部分资源已关闭，但遇到一些错误")
            
            return success
        except Exception as e:
            logger.error(f"关闭浏览器过程中发生未知错误: {str(e)}")
            return False

    def get_cookie_header(self):
        """生成Cookie请求头字符串"""
        if not self.cookies:
            return ""
        return "; ".join([f"{c['name']}={c['value']}" for c in self.cookies])